package com.github.defense.starter.client.launch;

import com.github.defense.common.config.DefenseConfig;
import com.github.defense.common.data.AppInfo;
import com.github.defense.common.data.Router;
import com.github.defense.common.enums.SceneEnum;
import com.github.defense.common.utils.NetUtils;
import com.github.defense.starter.common.event.DefenseEvent;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 加载项目路由信息并发送给defense-admin
 *
 * @Author: lettger
 * @Date: 2021/12/1 下午5:26
 */
@Slf4j
public class RequestMappingProcessor implements InitializingBean, ApplicationContextAware {

    private ApplicationContext applicationContext;

    private ApplicationEventPublisher applicationEventPublisher;

    private AppInfo appInfo = new AppInfo();

    public RequestMappingProcessor(final DefenseConfig config, final Environment environment, final ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
        String appName = environment.getProperty("spring.application.name");
        String port = environment.getProperty("server.port");
        if (Objects.isNull(config.getAdminAddr()) || Objects.isNull(appName)){
            throw new RuntimeException("*** defense config lack ***");
        }
        appInfo.setName(appName);
        appInfo.setPort(port);
        appInfo.setGroup(config.getGroup());
        appInfo.setGateway(false);
        appInfo.setHost(NetUtils.getAddress());
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = handlerMapping.getHandlerMethods();
        List<Router> routers = new ArrayList<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {
            RequestMappingInfo info = entry.getKey();
            HandlerMethod method = entry.getValue();
            // 忽略BasicErrorController /error 接口的注册
            if(method.getBean().equals("basicErrorController")){
                continue;
            }
            PatternsRequestCondition p = info.getPatternsCondition();
            Router router = new Router();
            Set<String> urls = p.getPatterns();
            for (String url : urls) {
                if (url.contains("{") && url.contains("}")) {
                    url = url.replaceAll("\\{[^}]*\\}", "[1-9][0-9]*");
                }
                router.getUrls().add(url);
            }
            ApiOperation apiOperation = AnnotationUtils.findAnnotation(method.getMethod(), ApiOperation.class);
            if (Objects.nonNull(apiOperation)) {
                router.setDesc(apiOperation.value());
            }
            RequestMethodsRequestCondition methodsCondition = info.getMethodsCondition();
            //取Get，Post .如果是RequestMapping的话，则是空
            Set<String> actions = methodsCondition.getMethods().stream().map(RequestMethod::toString).collect(Collectors.toSet());
            if (actions.size() == 0) {
                actions.add("*");
            }
            router.setActions(actions);
            routers.add(router);
        }
        routers.stream().forEach(res -> log.debug(res.toString()));
        appInfo.setRouters(routers);
        log.debug("This is application info :{}", appInfo);
        applicationEventPublisher.publishEvent(new DefenseEvent("", appInfo, SceneEnum.APP));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
